/*
 * Copyright (C) 2008,2009,2010,2011  Paulo Cesar Pereira de Andrade.
 *
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * Authors:
 *	Paulo Cesar Pereira de Andrade
 */

#ifndef _evm_h
#define _evm_h

#include "etypes.h"

#define ethrow(v)							\
    do {								\
	thread_self->except = v;					\
	return;								\
    } while (0)

#define set_rz()							\
    do {								\
	r0.t = t_rgz;							\
	r0.v.mpz = thr_zr;						\
    } while (0)
#define chk_rz()							\
    do {								\
	if (r0.t != t_rgz)						\
	    set_rz();							\
    } while (0)
#define set_rq()							\
    do {								\
	r0.t = t_rgq;							\
	r0.v.mpq = thr_qr;						\
    } while (0)
#define chk_rq()							\
    do {								\
	if (r0.t != t_rgq)						\
	    set_rq();							\
    } while (0)
#define set_rr()							\
    do {								\
	r0.t = t_rgr;							\
	r0.v.mpr = thr_rr;						\
    } while (0)
#define chk_rr()							\
    do {								\
	if (r0.t != t_rgr)						\
	    set_rr();							\
    } while (0)
#define chk_rdd()							\
    do {								\
	if (imag(r0.v.cdd) == 0.0) r0.t = t_float;			\
    } while (0)
#define chk_rd()							\
    do {								\
	if (imag(r0.v.cdd) != 0.0) r0.t = t_cdd;			\
    } while (0)
#define set_rqq()							\
    do {								\
	r0.t = t_rqq;							\
	r0.v.cqq = thr_qq;						\
    } while (0)
#define chk_rqq()							\
    do {								\
	if (r0.t != t_rqq)						\
	    set_rqq();							\
    } while (0)
#define set_rrr()							\
    do {								\
	r0.t = t_rgc;							\
	r0.v.mpc = thr_c;						\
    } while (0)
#define chk_rrr()							\
    do {								\
	if (mpfr_zero_p(thr_ri))					\
	    chk_rr();							\
	else if (r0.t != t_rgc)						\
	    set_rrr();							\
    } while (0)
#define set_chk_rrr()							\
    do {								\
	if (mpfr_zero_p(thr_ri))					\
	    set_rr();							\
	else								\
	    set_rrr();							\
    } while (0)
#define set_rr_chk_rrr()						\
    do {								\
	if (mpfr_zero_p(thr_ri))					\
	    chk_rr();							\
	else								\
	    set_rrr();							\
    } while (0)

/*
 * Types
 */
enum eexcept {
    except_segmentation_violation = -1,
    except_nothing = 0,
    except_out_of_memory,
    except_floating_point_error,
    except_out_of_bounds,
    except_null_dereference,
    except_type_mismatch,
    except_input_output_error,
    except_invalid_argument,
    except_not_a_number,
    except_not_an_integer,
    except_not_a_real_number,
    except_not_a_rational_number,
    except_not_a_32_bits_integer,
    except_not_a_finite_real_number,
    except_unhandled_exception,
};

/*
 * Prototypes
 */
extern eobject_t
evm(ethread_t *thread);

#if JITVM && DEBUG
extern void
evm_abort(void);
#endif

extern void
evm_raise(eint32_t exception) noreturn;

extern int
evm_float_zero_p(void);

extern void
evm_match_inheritance(eint32_t base, eint32_t type, eint32_t offset);

extern void
evm_ll(eint32_t offset);

extern void
evm_ld(eint32_t offset);

extern void
evm_ldt(eint32_t offset, eint32_t type);

extern void
evm_lb(eint32_t offset);

extern void
evm_lbt(eint32_t offset, eint32_t type);

extern void
evm_ls(eint32_t offset);

extern void
evm_lh(void);

extern void
evm_lhref(eint32_t arg);

extern void
evm_lv(eint32_t length);

extern void
evm_lvi(eint32_t offset);

extern void
evm_lvref(eint32_t length, eint32_t arg);

extern void
evm_lr(eint32_t base, eint32_t offset, eint32_t type);

extern void
evm_lrref(eint32_t base, eint32_t offset, eint32_t type, eint32_t arg);

extern void
evm_lc(eint32_t offset, eint32_t type);

extern void
evm_lref(void);

extern void
evm_pdt(eint32_t offset);

extern void
evm_pv(eint32_t length);

extern void
evm_pvi(eint32_t offset);

extern void
evm_pr(eint32_t base, eint32_t offset, eint32_t type);

extern void
evm_pp(void);

extern void
evm_anon(eint32_t type, eint32_t size);

extern void
evm_store_int8(eint8_t *object);

extern void
evm_store_int16(eint16_t *object);

extern void
evm_store_int32(eint32_t *object);

extern void
evm_store_int64(eint_t *object);

extern void
evm_store_float32(efloat32_t *object);

extern void
evm_store_float64(efloat_t *object);

extern void
evm_store_pointer_int(eobject_t *pointer);

extern void
evm_store_pointer_float(eobject_t *pointer);

extern void
evm_store_pointer_mpz(eobject_t *pointer);

extern void
evm_store_pointer_mpq(eobject_t *pointer);

extern void
evm_store_pointer_mpr(eobject_t *pointer);

extern void
evm_store_pointer_cdd(eobject_t *pointer);

extern void
evm_store_pointer_cqq(eobject_t *pointer);

extern void
evm_store_pointer_mpc(eobject_t *pointer);

extern void
evm_store_pointer_globalref(eobject_t *pointer);

extern void
evm_store_pointer(eobject_t *pointer);

extern void
evm_sd(eint32_t offset);

extern void
evm_sdt(eint32_t offset, eint32_t type);

extern void
evm_store_value(evalue_t *value);

extern void
evm_sb(eint32_t offset);

extern void
evm_sbt(eint32_t offset, eint32_t type);

extern void
evm_store_value_push(evalue_t *value);

extern void
evm_store_value_copy(evalue_t *value);

extern void
evm_ss(eint32_t offset);

extern void
evm_sst(eint32_t offset, eint32_t type);

extern void
evm_ssv(eint32_t offset);

extern void
evm_sh(void);

extern void
evm_shref(eint32_t arg);

extern void
evm_sv(eint32_t rank);

extern void
evm_svi(eint32_t offset);

extern void
evm_svref(eint32_t arg);

extern void
evm_sr(eint32_t base, eint32_t offset, eint32_t type);

extern void
evm_srref(eint32_t arg);

extern void
evm_sc(eint32_t offset, eint32_t type);

extern void
evm_sref(void);

extern eint32_t
evm_jt(void);

extern eint32_t
evm_jf(void);

extern eobject_t
evm_jh(eint32_t offset);

extern eobject_t
evm_jv(eint32_t offset);

extern void
evm_stack_resize(eint32_t length);

extern void
evm_enter(eint32_t l, eint32_t e, eint32_t ac, eint32_t lc, eint32_t *t);

extern void
evm_begin(eint32_t length);

extern void
evm_off(eint32_t length);

extern void
evm_push(void);

extern void
evm_pusht(eint32_t type);

extern void
evm_pushv(void);

extern void
evm_pop(void);

extern eobject_t
evm_call(eint32_t offset, eobject_t ip);

extern eobject_t
evm_meth(eint32_t offset, eint32_t type, eobject_t ip);

extern eobject_t
evm_ecm(eint32_t offset, eint32_t type, eobject_t ip);

extern void
evm_blt(eint32_t offset, eint32_t count);

extern eobject_t
evm_ret(eint32_t count);

extern eobject_t
evm_rett(eint32_t count, eint32_t type);

extern void	evm_v_ne(evalue_t *v);
extern void	evm_i_ne(evalue_t *v);
extern void	evm_d_ne(evalue_t *v);
extern void	evm_z_ne(evalue_t *v);
extern void	evm_q_ne(evalue_t *v);
extern void	evm_r_ne(evalue_t *v);
extern void	evm_dd_ne(evalue_t *v);
extern void	evm_qq_ne(evalue_t *v);
extern void	evm_rr_ne(evalue_t *v);
extern void	evm_a_ne(evalue_t *v);
extern void	evm_ne(void);

extern void	evm_v_lt(evalue_t *v);
extern void	evm_i_lt(evalue_t *v);
extern void	evm_d_lt(evalue_t *v);
extern void	evm_z_lt(evalue_t *v);
extern void	evm_q_lt(evalue_t *v);
extern void	evm_r_lt(evalue_t *v);
extern void	evm_lt(void);

extern void	evm_v_le(evalue_t *v);
extern void	evm_i_le(evalue_t *v);
extern void	evm_d_le(evalue_t *v);
extern void	evm_z_le(evalue_t *v);
extern void	evm_q_le(evalue_t *v);
extern void	evm_r_le(evalue_t *v);
extern void	evm_le(void);

extern void	evm_v_eq(evalue_t *v);
extern void	evm_i_eq(evalue_t *v);
extern void	evm_d_eq(evalue_t *v);
extern void	evm_z_eq(evalue_t *v);
extern void	evm_q_eq(evalue_t *v);
extern void	evm_r_eq(evalue_t *v);
extern void	evm_dd_eq(evalue_t *v);
extern void	evm_qq_eq(evalue_t *v);
extern void	evm_rr_eq(evalue_t *v);
extern void	evm_a_eq(evalue_t *v);
extern void	evm_eq(void);

extern void	evm_v_ge(evalue_t *v);
extern void	evm_i_ge(evalue_t *v);
extern void	evm_d_ge(evalue_t *v);
extern void	evm_z_ge(evalue_t *v);
extern void	evm_q_ge(evalue_t *v);
extern void	evm_r_ge(evalue_t *v);
extern void	evm_ge(void);

extern void	evm_v_gt(evalue_t *v);
extern void	evm_i_gt(evalue_t *v);
extern void	evm_d_gt(evalue_t *v);
extern void	evm_z_gt(evalue_t *v);
extern void	evm_q_gt(evalue_t *v);
extern void	evm_r_gt(evalue_t *v);
extern void	evm_gt(void);

extern void	evm_v_and(evalue_t *v);
extern void	evm_i_and(evalue_t *v);
extern void	evm_z_and(evalue_t *v);
extern void	evm_a_and(evalue_t *v);
extern void	evm_and(void);

extern void	evm_v_or(evalue_t *v);
extern void	evm_i_or(evalue_t *v);
extern void	evm_z_or(evalue_t *v);
extern void	evm_a_or(evalue_t *v);
extern void	evm_or(void);

extern void	evm_v_xor(evalue_t *v);
extern void	evm_i_xor(evalue_t *v);
extern void	evm_z_xor(evalue_t *v);
extern void	evm_a_xor(evalue_t *v);
extern void	evm_xor(void);

extern void	evm_i_mul2(evalue_t *v, eint32_t shift);
extern void	evm_a_mul2(evalue_t *v);
extern void	evm_mul2(void);

extern void	evm_i_div2(evalue_t *v, eint32_t shift);
extern void	evm_a_div2(evalue_t *v);
extern void	evm_div2(void);

extern void	evm_i_shl(evalue_t *v, eint32_t shift);
extern void	evm_a_shl(evalue_t *v);
extern void	evm_shl(void);

extern void	evm_i_shr(evalue_t *v, eint32_t shift);
extern void	evm_a_shr(evalue_t *v);
extern void	evm_shr(void);

extern void	evm_v_add(evalue_t *v);
extern void	evm_i_add(evalue_t *v);
extern void	evm_d_add(evalue_t *v);
extern void	evm_z_add(evalue_t *v);
extern void	evm_q_add(evalue_t *v);
extern void	evm_r_add(evalue_t *v);
extern void	evm_dd_add(evalue_t *v);
extern void	evm_qq_add(evalue_t *v);
extern void	evm_rr_add(evalue_t *v);
extern void	evm_a_add(evalue_t *v);
extern void	evm_add(void);

extern void	evm_v_sub(evalue_t *v);
extern void	evm_i_sub(evalue_t *v);
extern void	evm_d_sub(evalue_t *v);
extern void	evm_z_sub(evalue_t *v);
extern void	evm_q_sub(evalue_t *v);
extern void	evm_r_sub(evalue_t *v);
extern void	evm_dd_sub(evalue_t *v);
extern void	evm_qq_sub(evalue_t *v);
extern void	evm_rr_sub(evalue_t *v);
extern void	evm_a_sub(evalue_t *v);
extern void	evm_sub(void);

extern void	evm_v_mul(evalue_t *v);
extern void	evm_i_mul(evalue_t *v);
extern void	evm_d_mul(evalue_t *v);
extern void	evm_z_mul(evalue_t *v);
extern void	evm_q_mul(evalue_t *v);
extern void	evm_r_mul(evalue_t *v);
extern void	evm_dd_mul(evalue_t *v);
extern void	evm_qq_mul(evalue_t *v);
extern void	evm_rr_mul(evalue_t *v);
extern void	evm_a_mul(evalue_t *v);
extern void	evm_mul(void);

extern void	evm_v_div(evalue_t *v);
extern void	evm_i_div(evalue_t *v);
extern void	evm_d_div(evalue_t *v);
extern void	evm_z_div(evalue_t *v);
extern void	evm_q_div(evalue_t *v);
extern void	evm_r_div(evalue_t *v);
extern void	evm_dd_div(evalue_t *v);
extern void	evm_qq_div(evalue_t *v);
extern void	evm_rr_div(evalue_t *v);
extern void	evm_a_div(evalue_t *v);
extern void	evm_div(void);

extern void	evm_v_trunc2(evalue_t *v);
extern void	evm_i_trunc2(evalue_t *v);
extern void	evm_d_trunc2(evalue_t *v);
extern void	evm_z_trunc2(evalue_t *v);
extern void	evm_q_trunc2(evalue_t *v);
extern void	evm_r_trunc2(evalue_t *v);
extern void	evm_a_trunc2(evalue_t *v);
extern void	evm_trunc2(void);

extern void	evm_v_rem(evalue_t *v);
extern void	evm_i_rem(evalue_t *v);
extern void	evm_d_rem(evalue_t *v);
extern void	evm_z_rem(evalue_t *v);
extern void	evm_q_rem(evalue_t *v);
extern void	evm_r_rem(evalue_t *v);
extern void	evm_a_rem(evalue_t *v);
extern void	evm_rem(void);

extern void	evm_v_atan2(evalue_t *v);
extern void	evm_i_atan2(evalue_t *v);
extern void	evm_d_atan2(evalue_t *v);
extern void	evm_z_atan2(evalue_t *v);
extern void	evm_q_atan2(evalue_t *v);
extern void	evm_r_atan2(evalue_t *v);
extern void	evm_dd_atan2(evalue_t *v);
extern void	evm_qq_atan2(evalue_t *v);
extern void	evm_rr_atan2(evalue_t *v);
extern void	evm_a_atan2(evalue_t *v);
extern void	evm_atan2(void);

extern void	evm_v_pow(evalue_t *v);
extern void	evm_i_pow(evalue_t *v);
extern void	evm_d_pow(evalue_t *v);
extern void	evm_z_pow(evalue_t *v);
extern void	evm_q_pow(evalue_t *v);
extern void	evm_r_pow(evalue_t *v);
extern void	evm_dd_pow(evalue_t *v);
extern void	evm_qq_pow(evalue_t *v);
extern void	evm_rr_pow(evalue_t *v);
extern void	evm_a_pow(evalue_t *v);
extern void	evm_pow(void);

extern void	evm_v_hypot(evalue_t *v);
extern void	evm_i_hypot(evalue_t *v);
extern void	evm_d_hypot(evalue_t *v);
extern void	evm_z_hypot(evalue_t *v);
extern void	evm_q_hypot(evalue_t *v);
extern void	evm_r_hypot(evalue_t *v);
extern void	evm_dd_hypot(evalue_t *v);
extern void	evm_qq_hypot(evalue_t *v);
extern void	evm_rr_hypot(evalue_t *v);
extern void	evm_a_hypot(evalue_t *v);
extern void	evm_hypot(void);

extern void	evm_v_complex(evalue_t *v);
extern void	evm_i_complex(evalue_t *v);
extern void	evm_d_complex(evalue_t *v);
extern void	evm_z_complex(evalue_t *v);
extern void	evm_q_complex(evalue_t *v);
extern void	evm_r_complex(evalue_t *v);
extern void	evm_complex(void);

extern void	evm_i_inc(void);
extern void	evm_z_inc(void);
extern void	evm_inc(void);

extern void	evm_i_dec(void);
extern void	evm_z_dec(void);
extern void	evm_dec(void);

extern void
evm_bool(void);

extern void
evm_not(void);

extern void
evm_com(void);

extern void
evm_inv(void);

extern void
evm_neg(void);

extern void
evm_signbit(void);

extern void
evm_signum(void);

extern void
evm_rational(void);

extern void
evm_integer_p(void);

extern void
evm_rational_p(void);

extern void
evm_float_p(void);

extern void
evm_real_p(void);

extern void
evm_complex_p(void);

extern void
evm_number_p(void);

extern void
evm_finite_p(void);

extern void
evm_inf_p(void);

extern void
evm_nan_p(void);

extern void
evm_num(void);

extern void
evm_den(void);

extern void
evm_real(void);

extern void
evm_imag(void);

extern void
evm_arg(void);

extern void
evm_conj(void);

extern void
evm_floor(void);

extern void
evm_trunc(void);

extern void
evm_round(void);

extern void
evm_ceil(void);

extern void
evm_abs(void);

extern void
evm_sqrt(void);

extern void
evm_cbrt(void);

extern void
evm_sin(void);

extern void
evm_cos(void);

extern void
evm_tan(void);

extern void
evm_asin(void);

extern void
evm_acos(void);

extern void
evm_atan(void);

extern void
evm_sinh(void);

extern void
evm_cosh(void);

extern void
evm_tanh(void);

extern void
evm_asinh(void);

extern void
evm_acosh(void);

extern void
evm_atanh(void);

extern void
evm_proj(void);

extern void
evm_exp(void);

extern void
evm_log(void);

extern void
evm_log2(void);

extern void
evm_log10(void);

extern void
evm_new(eint32_t type, eint32_t size);

extern void
evm_mv(eint32_t length);

extern void
evm_vnew(eint32_t type, eint32_t rank);

extern void
evm_vnewi(eint32_t type, eint32_t length, eint32_t rank, eint32_t *dims);

extern void
evm_vmv(eint32_t length);

extern void
evm_renew(void);

extern void
evm_renewi(eint32_t length);

extern void
evm_fref(eint32_t offset, eint32_t count, eint32_t bits);

extern eobject_t
evm_apply(eint32_t length, eobject_t ip);

extern void
evm_aret(void);

extern void
evm_szof(void);

extern void
evm_szofva(void);

extern void
evm_szofdim(void);

extern void
evm_szofdimi(eint32_t offset);

extern void
evm_tpof(void);

extern void
evm_subtpof(void);

extern void
evm_rankof(void);

extern void
evm_vas(eint32_t offset);

extern void
evm_val(eint32_t offset);

extern void
evm_lva(eint32_t length);

extern void
evm_lvai(eint32_t length, eint32_t offset);

extern void
evm_sva(eint32_t length);

extern void
evm_svai(eint32_t length, eint32_t offset);

extern void
evm_pva(eint32_t length);

extern void
evm_try(eobject_t ip);

extern void
evm_catch(void);

extern eobject_t
evm_throw(eint32_t reload);

extern void
evm_unwind(void);

extern void
evm_exit(void);

extern void
evm_setup(eint32_t offset, eint32_t type);

extern void
evm_intp_0(void);

extern void
evm_intp_v(void);

extern void
evm_intp_n1(void);

extern void
evm_realp_v(void);

extern void
evm_nump_0(void);

extern void
evm_nump_v(void);

void
init_vm(void);

#endif /* _evm_h */
